package cup.common.fs;


import cup.common.utils.DateUtil;
import cup.core.log.exception.ServiceException;
import io.minio.MinioClient;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.errors.*;
import io.minio.messages.DeleteError;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.sql.rowset.serial.SerialException;
import java.io.*;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.Date;
import java.util.List;

/**
 * 文件系统上传下载帮助类
 * 所有下载不能直接提供文件服务器地址
 *
 * @Author zhaoliang
 */
@Component
public class FsHelper {

	@Value("${minio.url}")
	private String url;
	@Value("${minio.accessKey}")
	private String accessKey;
	@Value("${minio.secretKey}")
	private String secretKey;

	/**
	 * 上传文件，避免同名文件不能上传，在文件名前加上32位uuid_
	 *
	 * @return
	 * @throws Exception
	 */
	public String uploadFile(String bucket, MultipartFile file) {
		try {
			return this.uploadFile(bucket, file.getOriginalFilename(), file.getInputStream(), file.getSize(), false,file.getContentType());
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	/**
	 * 上传文件，避免同名文件不能上传，在文件名前加上32位uuid_
	 *
	 * @return
	 * @throws Exception
	 */
	public String uploadFile(String bucket, File file) {
		try {
			return this.uploadFile(bucket, file.getName(), new FileInputStream(file), file.length(), false,"");
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	/**
	 * 上传文件
	 *
	 * @param bucket
	 * @param file
	 * @param nameOrigin 是否按原始文件名存储
	 */
	public String uploadFile(String bucket, File file, boolean nameOrigin) {
		try {
			return this.uploadFile(bucket, file.getName(), new FileInputStream(file), file.length(), true,"");
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	private static final String SDF = "yyyyMMddHHmmssSSS";


	public String uploadFile(String bucket, String filename, InputStream is, long size, boolean nameOrigin,String contentType) {
		try {
			MinioClient minioClient = this.getClient();
			boolean isExist = minioClient.bucketExists(bucket);
			if (!isExist) {
				minioClient.makeBucket(bucket);
			}
			PutObjectOptions options = new PutObjectOptions(size, -1);
			if (StringUtils.isNotEmpty(contentType)){
				options.setContentType(contentType);
			}

			if (!nameOrigin) {
				//防止文件名冲突，导致无法上传，加入时间戳前缀
				//String uuid = UUID.randomUUID().toString().replaceAll("-", "");
				String flag =DateUtil.newDateFormatToStr(new Date(),SDF);
				filename = flag.substring(0, flag.length() - 2) + "_" + filename;
			}
			minioClient.putObject(bucket, filename, is, options);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
		return filename;
	}

	/**
	 * 根据存储的加工后的文件名下载文件，下载后的文件名为原始文件名
	 *
	 * @param bucket
	 * @param storeFileName 存储的文件名
	 * @return
	 * @throws Exception
	 */
	public ResponseEntity<byte[]> download(String bucket, String storeFileName) {
		return download(bucket, storeFileName, false);
	}

	/**
	 * 根据文件原名下载
	 *
	 * @param bucket
	 * @param storeFileName
	 * @param nameOrigin    是否按照原始文件名下载
	 * @return
	 * @throws Exception
	 */
	public ResponseEntity<byte[]> download(String bucket, String storeFileName, boolean nameOrigin) {
		try {
			InputStream is = this.getClient().getObject(bucket, storeFileName);
			if (is == null) {
				throw new SerialException("文件不存在:" + storeFileName);
			}
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			int b;
			while ((b = is.read()) != -1) {
				baos.write(b);
			}
			HttpHeaders headers = new HttpHeaders();
			//读取文件字节数组
			byte[] res = baos.toByteArray();
			headers.add("Content-Type", "application/octet-stream;charset=utf-8");
			headers.add("Connection", "close");
			headers.add("Accept-Ranges", "bytes");
			String filename = nameOrigin ? storeFileName : getFilename(storeFileName);
			headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
			//将文件字节数组，header，状态码封装到ResponseEntity
			return new ResponseEntity<byte[]>(res, headers, HttpStatus.OK);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	public InputStream getInputStream(String bucket, String storeFileName) {
		try {
			return this.getClient().getObject(bucket, storeFileName);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	/**
	 * 根据存储名字，获取原始文件名
	 *
	 * @param storeFileName
	 * @return
	 */
	public String getFilename(String storeFileName) {
		//文件真实名字
		//flag.substring(0, flag.length() - 2) + "_" + filename;
		if(storeFileName != null && storeFileName.length() > 16 && "_".equals(storeFileName.substring(15,16))) {
			return storeFileName.substring(16);
		}
		return storeFileName;
	}

	public MinioClient getClient() {
		try {
			return new MinioClient(url, accessKey, secretKey);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	/**
	 * 批量删除文件
	 *
	 * @param bucket
	 * @param storeFileNames
	 * @param nameOrigin
	 */
	public void removeFiles(String bucket, List<String> storeFileNames, boolean nameOrigin) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {

		if (storeFileNames == null || storeFileNames.size() < 1)
			return;

		if (!nameOrigin) {
			storeFileNames.forEach(storeFileName -> {
				storeFileName = getFilename(storeFileName);
			});
		}

		// 删除bucket里的多个对象
		for (Result<DeleteError> errorResult:  this.getClient().removeObjects(bucket, storeFileNames)) {
			DeleteError error = errorResult.get();
		}
	}

	/**
	* @Description: 返回base64
	* @Author: LuanKay
	* @Date: 2022/7/20
	*/
	public String resultBase(String storeFileName){
		InputStream is = null;
		try {
			is = this.getClient().getObject(FsConstant.BUCKET.BUCKET_CQZJ_BOOT, storeFileName);
			byte[] bytes = IOUtils.toByteArray(is);
			String encoded = Base64.getEncoder().encodeToString(bytes);
			return encoded;
		} catch (ErrorResponseException e) {
//			throw new RuntimeException(e);
			throw new ServiceException("minio认证失败");
		} catch (InsufficientDataException e) {
			throw new RuntimeException(e);
		} catch (InternalException e) {
			throw new RuntimeException(e);
		} catch (InvalidBucketNameException e) {
			throw new RuntimeException(e);
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);
		} catch (InvalidResponseException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		} catch (XmlParserException e) {
			throw new RuntimeException(e);
		}
	}


	/**
	 * 图片预览地址 调用这个方法的时候记得设置桶权限为public
	 */

	public String resultPreview(String storeFileName) {
		MinioClient minioClient = this.getClient();
		try {
			//默认预览有效期604800天(minio最大值)
			return minioClient.presignedGetObject(FsConstant.BUCKET.BUCKET_CQZJ_BOOT, storeFileName);
//			return minioClient.getPresignedObjectUrl(Method.GET, FsConstant.BUCKET.BUCKET_CQZJ_BOOT, storeFileName, 604800, new HashMap<>());
		} catch (ErrorResponseException e) {
			throw new RuntimeException(e);
		} catch (InsufficientDataException e) {
			throw new RuntimeException(e);
		} catch (InternalException e) {
			throw new RuntimeException(e);
		} catch (InvalidBucketNameException e) {
			throw new RuntimeException(e);
		} catch (InvalidExpiresRangeException e) {
			throw new RuntimeException(e);
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);
		} catch (InvalidResponseException e) {
			throw new RuntimeException(e);
		} catch (IOException e) {
			throw new RuntimeException(e);
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		} catch (XmlParserException e) {
			throw new RuntimeException(e);
		}
	}
}
